home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’87 / Source ƒ.sit / Source ƒ / C ƒ / CITADEL BBS 'C' SRC / MODEM.C < prev    next >
C/C++ Source or Header  |  1987-01-14  |  36KB  |  1,249 lines

  1. /************************************************************************/
  2. /*                modem.c                 */
  3. /*                                    */
  4. /*        modem code for Citadel bulletin board system        */
  5. /*    NB: this code is rather machine-dependent:  it will typically    */
  6. /*    need some twiddling for each new installation.            */
  7. /*                  82Nov05 CrT                */
  8. /************************************************************************/
  9.  
  10. #define NO_CRC
  11.  
  12. /************************************************************************/
  13. /*                history                 */
  14. /*                                    */
  15. /* 85Nov09 HAW    Warning bell before timeout.                */
  16. /* 85Oct27 HAW    Cermetek support eliminated.                */
  17. /* 85Oct18 HAW    2400 support.                        */
  18. /* 85Sep15 HAW    Put limit in ringSysop().                */
  19. /* 85Aug17 HAW    Update for gotCarrier().                */
  20. /* 85Jul05 HAW    Insert fix code (Brian Riley) for 1200 network.     */
  21. /* 85Jun11 HAW    Fix readFile to recognize loss of carrier.        */
  22. /* 85May27 HAW    Code for networking time out.                */
  23. /* 85May06 HAW    Code for daily timeout.                 */
  24. /* 85Mar07 HAW    Stick in Sperry PC mods for MSDOS.            */
  25. /* 85Feb22 HAW    Upload/download implemented.                */
  26. /* 85Feb20 HAW    IMPERVIOUS flag implemented.                */
  27. /* 85Feb17 HAW    Baud change functions installed.            */
  28. /* 85Feb09 HAW and Sr.    Chat bug analyzed by Sr.            */
  29. /* 85Jan16 JLS    fastIn modified for CR being first character from modem.*/
  30. /* 85Jan04 HAW    Code added but not tested for new WC functions.     */
  31. /* 84Sep12 HAW    Continue massacre of portability -- bye, pMIReady.    */
  32. /* 84Aug30 HAW    Wheeee!!  MS-DOS time!!                 */
  33. /* 84Aug22 HAW    Compilation directive for 8085 chips inserted.        */
  34. /* 84Jul08 JLS & HAW ReadFile() fixed for the 255 rollover.        */
  35. /* 84Jul03 JLS & HAW All references to putCh changed to putChar.    */
  36. /* 84Jun23 HAW & JLS Local unused variables zapped.            */
  37. /* 84Mar07 HAW    Upgrade to BDS 1.50a begun.                */
  38. /* 83Mar01 CrT    FastIn() ignores LFs etc -- CRLF folks won't be trapped.*/
  39. /* 83Feb25 CrT    Possible fix for backspace-in-message-entry problem.    */
  40. /* 83Feb18 CrT    fastIn() upload mode cutting in on people.  Fixed.    */
  41. /* 82Dec16 dvm    modemInit revised for FDC-1, with kludge for use with    */
  42. /*        Big Board development system                */
  43. /* 82Dec06 CrT    2.00 release.                        */
  44. /* 82Nov15 CrT    readfile() & sendfile() borrowed from TelEdit.c     */
  45. /* 82Nov05 CrT    Individual history file established            */
  46. /************************************************************************/
  47.  
  48. #include "ctdl.h"
  49.  
  50. /************************************************************************/
  51. /*                Contents                */
  52. /*                                    */
  53. /*    BBSCharReady()        returns true if user input is ready    */
  54. /*    check_CR()        scan input for carriage returns     */
  55. /*    doWC()            initializes WC transfers        */
  56. /*  #    fastIn()        kludge code compiling other stuff inline*/
  57. /*    Find_baud()        does flip flop search for baud        */
  58. /*    getCh()         bottom-level console-input filter    */
  59. /*  #    getMod()        bottom-level modem-input   filter    */
  60. /*    iChar()         top-level user-input function        */
  61. /*    interact()        chat mode                */
  62. /*    interpret()        interprets a configuration routine    */
  63. /*    KBReady()        returns TRUE if a console char is ready */
  64. /*    MIReady()        check MS-DOS interrupt for data     */
  65. /*    modIn()         returns a user char            */
  66. /*    modemInit()        top-level initialize-all-modem-stuff    */
  67. /*  #    mputChar()        Try not to allow ^C aborts ...        */
  68. /*    oChar()         top-level user-output function        */
  69. /*  #    outMod()        bottom-level modem output        */
  70. /*    pause()         pauses for N/100 seconds        */
  71. /*    readFile()        accept a file using WC protocol     */
  72. /*    recieve()        read modem char or time out        */
  73. /*    ringSysop()        signal chat-mode request        */
  74. /*    sendWCChar()        send file with WC-protocol handshaking    */
  75. /*    shortPause()        Brian Riley's 1200 fix code.        */
  76. /*                                    */
  77. /*    # == routines you should certainly check when porting system    */
  78. /************************************************************************/
  79.  
  80. /************************************************************************/
  81. /*        External variable declarations in MODEM.C        */
  82. /************************************************************************/
  83. char justLostCarrier = FALSE;    /* Modem <==> rooms connection        */
  84. char newCarrier      = FALSE;    /* Just got carrier            */
  85. char onConsole;         /* Who's in control?!?            */
  86. int  outPut = NORMAL;
  87. unsigned char modStat;        /* Whether modem had carrier LAST time    */
  88.                 /* you checked.             */
  89. static unsigned         /* Place to save data on check ...    */
  90.        char InData;
  91.  
  92. static char KBData = 0;     /* Data from keyboard            */
  93. char whichIO = CONSOLE;     /* CONSOLE or MODEM            */
  94. #ifdef NEED_VISIBLE
  95. char visibleMode;        /* make non-printables visible?     */
  96. #endif
  97. char haveCarrier;        /* set if DCD == TRUE            */
  98. char textDownload;        /* read host files, TRUE => ASCII    */
  99. char echo;            /* Either NEITHER, CALLER, or BOTH    */
  100. char echoChar;            /* What to echo with if echo == NEITHER */
  101. char usingWCprotocol;        /* True during WC protocol transfers    */
  102. int  upDay;            /* Day system was brought up        */
  103. char nextDay;            /* Come down tomorrow rather than today?*/
  104. int  timeCrash;
  105. char IBMinterrupts = FALSE;
  106. char anyEcho = TRUE;
  107. char warned;
  108.  
  109. #ifdef MSDOS
  110. #define COMBUF_SIZE    5000
  111.  
  112. unsigned char old_inta01;
  113. unsigned char combuf[COMBUF_SIZE];
  114. int cptr, head;
  115. #endif
  116.  
  117. static struct regval writereg = {0, 0, 0, 0, 0, 0, 0, 0};
  118. static struct regval readreg  = {0x0100, 0, 0, 0, 0, 0, 0, 0};
  119. static struct regval inreg;
  120.  
  121. static char *rates[] = {
  122.             "300",
  123.             "1200",
  124.             "2400",
  125.             "Unknown"} ;
  126.  
  127. /* WC stuff */
  128. #define MINUTE    60
  129. unsigned char WCSecNum;
  130. char          WCBuf[SECTSIZE];
  131. unsigned char WCChar;
  132. char          WCError;
  133. char          CRCmode;
  134.  
  135. /************************************************************************/
  136. /*        External variable definitions for MODEM.C        */
  137. /************************************************************************/
  138. extern struct msgB    msgBuf;     /* Message buffer        */
  139. extern struct config    cfg;        /* Configuration variables    */
  140. extern FILE        *upfd;
  141. extern char        termLF;     /* Linefeeds?            */
  142. extern char        prevChar;    /* previous char        */
  143. extern char        termUpper;    /* Handle only UC?        */
  144. extern char        debug;        /* debug flag            */
  145. extern char        outFlag;    /* output flag            */
  146. extern char        ExitToMsdos;    /* Kill program flag        */
  147. extern int        exitValue;
  148. extern char        inNet;
  149.  
  150. extern int        acount;
  151. #define AUDIT        9000
  152. extern char        audit[AUDIT];
  153.  
  154. /************************************************************************/
  155. /*        External function definitions for MODEM.C        */
  156. /************************************************************************/
  157. unsigned char inportb();
  158. unsigned char interpret();
  159. unsigned char modIn();
  160. unsigned char bdos();
  161. unsigned char inp();
  162. char          getCh();
  163. char          KBReady();
  164. long          getNumber();
  165. char          getMod();
  166.  
  167. /************************************************************************/
  168. /*        The principal dependencies:                */
  169. /*                                    */
  170. /*  iChar   modIn                    outMod        */
  171. /*        modIn   getMod  getCh   mIReady kBReady outMod  carrDetect    */
  172. /*            getMod                        */
  173. /*                getCh                    */
  174. /*                    mIReady                */
  175. /*                        kBReady            */
  176. /*                                carrDetect    */
  177. /*                                    */
  178. /*  oChar                        outMod        */
  179. /*                            outMod  mOReady    */
  180. /************************************************************************/
  181.  
  182.  
  183. /************************************************************************/
  184. /*    BBSCharReady() returns TRUE if char is available from user    */
  185. /*    NB: user may be on modem, or may be sysop in CONSOLE mode    */
  186. /************************************************************************/
  187. char BBSCharReady()
  188. {
  189.     return (((haveCarrier && whichIO == MODEM) && MIReady()) ||
  190.        (whichIO == CONSOLE    &&   KBReady()));
  191. }
  192.  
  193. /************************************************************************/
  194. /*    doWC() initializes the system for a WC download         */
  195. /************************************************************************/
  196. doWC(mode)
  197. char mode;
  198. {
  199.     int i, m;
  200.  
  201.     if (mode == STARTUP) {    /* Setup globals for coming fun     */
  202.     if (!cfg.debug && !inNet)
  203.         printf("A WC download is in progress\n");
  204.     WCSecNum = 1;
  205.     WCChar     = 0;
  206.     WCError  = FALSE;
  207.     CRCmode  = FALSE;
  208.     i = 0;
  209.     while (1) {
  210.         m = receive(MINUTE);
  211.         if (m == ERROR || m == CAN)
  212.         return FALSE;
  213.         if (m == NAK)
  214.         return TRUE;
  215.         if (m == 'C') {
  216.         CRCmode = TRUE;
  217.         printf("CRC mode\n");
  218.         return TRUE;
  219.         }
  220.         if (++i == ERRORMAX)
  221.         return FALSE;
  222.     }
  223.     }
  224.     else {
  225.     if (WCError) return;
  226.     for (; WCChar != 0 && sendWCChar(' '); )
  227.         ;        /* Final sector */
  228.     for (i = 0; i < ERRORMAX; i++) {
  229.         outMod(EOT);
  230.         if (receive(MINUTE) == ACK || !haveCarrier) break;
  231.     }
  232.     }
  233. }
  234.  
  235. /************************************************************************/
  236. /*    Find_Baud() Finds the baud from sysop and user supplied data.    */
  237. /************************************************************************/
  238. Find_baud()
  239. {
  240.     char noGood = TRUE;
  241.     int  Time = 0;
  242.     int  baudRunner;            /* Only try for 60 seconds    */
  243.  
  244.     while (MIReady())    getMod();        /* Clear garbage    */
  245.     baudRunner = 0;
  246.     while (gotCarrier() && noGood && Time < 120) {
  247.     Time++;
  248.     interpret(cfg.pBauds[baudRunner]);
  249.     noGood = check_CR();
  250.     if (noGood) baudRunner = (baudRunner + 1) % (cfg.sysBaud + 1);
  251.     }
  252.     logMessage(BAUD, rates[baudRunner], FALSE);
  253.     return !noGood;
  254. }
  255.  
  256. /************************************************************************/
  257. /*    check_CR() Checks for CRs from the data port for half a second. */
  258. /************************************************************************/
  259. check_CR()
  260. {
  261.     int i;
  262.  
  263.     for (i = 0; i < 50; i++) {
  264.     pause(1);
  265.     if (MIReady())
  266.         if (getMod() == '\r')
  267.         return FALSE;
  268.     }
  269.     return TRUE;
  270. }
  271.  
  272. /************************************************************************/
  273. /*    getCh() reads a console char                    */
  274. /*        In CONSOLE mode, CRs are changed to newlines        */
  275. /*        Rubouts are changed to backspaces                */
  276. /*    Returns:    resulting char                    */
  277. /************************************************************************/
  278. char getCh()
  279. {
  280.     char temp;
  281.  
  282.     if (KBData != 0) {
  283.     temp = KBData;
  284.     KBData = 0;
  285.     return temp;
  286.     }
  287.     return bdos(7);
  288. }
  289.  
  290. /************************************************************************/
  291. /*    getMod() is bottom-level modem-input routine            */
  292. /*      kills any parity bit                        */
  293. /*      rubout            -> backspace            */
  294. /*      CR                -> newline            */
  295. /*      other nonprinting chars    -> blank            */
  296. /*    Returns: result                         */
  297. /************************************************************************/
  298. char getMod()
  299. {
  300.     return inp() & 0x7F;
  301. }
  302.  
  303. /************************************************************************/
  304. /*    iChar() is the top-level user-input function -- this is the    */
  305. /*    function the rest of Citadel uses to obtain user input        */
  306. /************************************************************************/
  307. char iChar()
  308. {
  309.     char c;
  310.  
  311.     if (justLostCarrier)   return 0;    /* ugly patch    */
  312.  
  313.     c = cfg.filter[modIn()];
  314.  
  315.  
  316. /* AUDIT CODE */
  317. if (!onConsole) {
  318. audit[acount] = c;
  319. acount = (acount + 1) % AUDIT;
  320. }
  321.  
  322.  
  323.  
  324.     switch (echo) {
  325.     case BOTH:
  326.     if (haveCarrier) {
  327.         if (c == '\n')    doCR();
  328.         else        outMod(c);
  329.     }
  330.     mputChar(c);     /* Let putChar decide if it should go on console */
  331.     break;
  332.     case CALLER:
  333.     if (whichIO == MODEM) {
  334.         if (c == '\n')    doCR();
  335.         else        outMod(c);
  336.     } else {
  337.         mputChar(c);
  338.     }
  339.     break;
  340.     case NEITHER:
  341.     if (echoChar != '\0') {
  342.         if (whichIO == MODEM) {
  343.         if (c == '\n') doCR();
  344.         else           outMod(echoChar);
  345.         }
  346.         else {
  347.         if (c == '\n') doCR();
  348.         else           mputChar(echoChar);
  349.         }
  350.     }
  351.     break;
  352.     }
  353.     return(c);
  354. }
  355.  
  356. /************************************************************************/
  357. /*    inp() reads data from port.  Should not be called if there is    */
  358. /*    no data present (for good reason).                */
  359. /************************************************************************/
  360. unsigned char inp()
  361. {
  362.     unsigned char temp;
  363.     int k;
  364.  
  365.     if (!cfg.IBM_or_clone) {
  366.     temp = InData;
  367.     InData = 0;
  368.     return temp;
  369.     }
  370.     else {
  371.     if (!IBMinterrupts)
  372.         return inportb(cfg.modem_data);
  373.     k = cptr;
  374.     if (k==head)
  375.         return 0;
  376.     if (k>head)
  377.         return (combuf[head++]);
  378.     if (head < COMBUF_SIZE)
  379.         return (combuf[head++]);
  380.     head = 0;
  381.     if (head < k)
  382.         return (combuf[head++]);
  383.     printf("ERROR\n");
  384.     }
  385. }
  386.  
  387. /************************************************************************/
  388. /*    interact() allows the sysop to interact directly with        */
  389. /*    whatever is on the modem.                   dvm 9-82 */
  390. /************************************************************************/
  391. interact()
  392. {
  393.     char c, lineEcho, lineFeeds, localEcho;
  394.     int  yr, dy, hr, mn;
  395.     char last = 0;
  396.     char *mon;
  397.  
  398.     c = 0;
  399.     printf(" Direct modem-interaction mode\n");
  400.     lineEcho    = conGetYesNo("Echo to modem"      );
  401.     localEcho    = conGetYesNo("Echo keyboard"      );
  402.     lineFeeds    = conGetYesNo("Echo CR as CRLF"   );
  403.     printf("<ESC> to exit\n");
  404.  
  405.     /* incredibly ugly code.  Rethink sometime: */
  406.     while (c != SPECIAL) {
  407.     c = 0;
  408.  
  409.     if (MIReady()) {
  410.         c = getMod();
  411.         if (c != '\r') c = cfg.filter[c];
  412.         if (c != '\r') {
  413.         if (lineEcho && c != ESC)   outMod(c);
  414.         mputChar(c);
  415.         } else {
  416.         if (!lineFeeds) {
  417.             if (lineEcho) outMod('\r');
  418.             mputChar('\r');
  419.             mputChar('\n');
  420.         } else {
  421.             if (lineEcho) {
  422.             outMod('\r');
  423.             outMod('\n');
  424.             }
  425.             mputChar('\r');
  426.             mputChar('\n');
  427.         }
  428.         }
  429.     }
  430.     else if (KBReady()) {
  431.     /*  c  = cfg.filter[getCh()];    */
  432.         if ((c  = getCh() & 0x7f) == '\r') c = '\n';
  433.         if (c != NEWLINE) {
  434.         if (localEcho)    mputChar(c);
  435.         if (c != ESC && c != '\\')   outMod(c);
  436.         else {
  437.             if (last == '\\') {
  438.             outMod(c);
  439.             c = 0;
  440.             }
  441.         }
  442.         last = c;
  443.         } else {
  444.         if (!lineFeeds) {
  445.             if (localEcho) mputChar('\r');
  446.             outMod('\r');
  447.         } else {
  448.             if (lineEcho) {
  449.             mputChar('\r');
  450.             mputChar('\n');
  451.             }
  452.             outMod('\r');
  453.             outMod('\n');
  454.         }
  455.         }
  456.     }
  457.     else if (cfg.netParticipant) {
  458.         getDate(&yr, &mon, &dy, &hr, &mn);
  459.         if (dy % cfg.dayDiv == 0)
  460.         if (hr == cfg.netHour - 1 && mn >= 55 && !warned) {
  461.             warned = TRUE;
  462.             outFlag = IMPERVIOUS;
  463.             mPrintf("\n WARNING: System going down at %d AM\n ",
  464.                                 cfg.netHour);
  465.             outFlag = OUTOK;
  466.         }
  467.         else if (inNetTime(hr, mn)) {
  468.             c = SPECIAL;
  469.         }
  470.     }
  471.     }
  472. }
  473.  
  474. /************************************************************************/
  475. /*    interpret() interprets a configuration routine            */
  476. /*    Returns byte value computed                    */
  477. /************************************************************************/
  478. unsigned char interpret(instruction)
  479. int instruction;
  480. {
  481.     union {
  482.     unsigned char **pp;
  483.     int     *pi;
  484.     unsigned char *pc;
  485.     } instr;
  486.     unsigned char accum, bite;    /* our sole accumulator */
  487.     char      *prompt;
  488.     int       lowLim, topLim;
  489.     int       comint();
  490.                 /* Deactivate IBM interrupts for a moment */
  491.     if (cfg.IBM_or_clone && IBMinterrupts) {
  492.     bite = inportb(INTA01);
  493.     outportb(INTA01, old_inta01);
  494.     }
  495.  
  496.     instr.pc = &cfg.codeBuf[instruction];
  497.     while (TRUE) {
  498.     switch (*instr.pc++) {
  499.     case RET:
  500.             if (cfg.IBM_or_clone && IBMinterrupts)
  501.                 outportb(INTA01, bite);
  502.             return accum;                break;
  503.     case ANDI:    accum           &= *instr.pc++;        break;
  504.     case INP:    accum        = inportb(*instr.pi++); break;
  505.     case XORI:    accum           ^= *instr.pc++;        break;
  506.     case LOAD:    accum        = *(*instr.pp++);    break;
  507.     case LOADI:    accum        = *instr.pc++;        break;
  508.     case LOADX:    accum        = cfg.scratch[*instr.pc++]; break;
  509.     case ORI:    accum           |= *instr.pc++;        break;
  510.     case OUTP:    outportb(*instr.pi++, accum);        break;
  511.     case PAUSEI:    pause(*instr.pc++);            break;
  512.     case STORE:    *(*instr.pp++)    = accum;        break;
  513.     case STOREX:    cfg.scratch[*instr.pc++] = accum;    break;
  514.     case OPRNUMBER:
  515.         prompt  = instr.pc;
  516.         while(*instr.pc++);     /* step over prompt    */
  517.         lowLim = *instr.pc++;
  518.         topLim = *instr.pc++;
  519.         accum  = (int) getNumber(prompt, (ulong) lowLim, (ulong) topLim);
  520.         break;
  521.     case OUTSTRING:
  522.         while(*instr.pc) {
  523.         pause(5);    /* SmartModem can't handle 300 baud    */
  524.         outMod(*instr.pc++);    /* output string */
  525.         }
  526.         instr.pc++;                 /* skip null     */
  527.         break;
  528.     default:
  529.         printf("intrp-no opcod%d", *(instr.pc-1));
  530.         break;
  531.     }
  532.     }
  533.     if (cfg.IBM_or_clone && IBMinterrupts)
  534.     outportb(INTA01, bite);
  535. }
  536.  
  537. /************************************************************************/
  538. /*    KBReady() returns TRUE if a console char is ready        */
  539. /************************************************************************/
  540. char KBReady()
  541. {
  542.     if (KBData) return TRUE;
  543.     return (KBData = bdos(6, 0xFF));
  544. }
  545.  
  546. /************************************************************************/
  547. /*    MIReady() Ostensibly checks to see if input from modem ready    */
  548. /************************************************************************/
  549. MIReady()
  550. {
  551.     unsigned char regVal;
  552.  
  553.     if (!cfg.IBM_or_clone) {
  554. #define MOD_FUNC    0x4e
  555. #define BIOSSEG     0x40
  556.     if (InData != 0)
  557.         return TRUE;
  558.     if (farcall(MOD_FUNC, BIOSSEG, &readreg, &inreg) & 1)
  559.         return FALSE;
  560.     InData = inreg.ax & 0xFF;   /* Strip high byte of result    */
  561.     return TRUE;
  562.     }
  563.     else {
  564.     if (!IBMinterrupts) {
  565.         regVal = inportb(cfg.modem_status) & 1;
  566.         return regVal;
  567.     }
  568.     /* else */
  569.     return (cptr != head);
  570.     }
  571. }
  572.  
  573. /************************************************************************/
  574. /*    modemClose() Responsible for shutting down I/O            */
  575. /************************************************************************/
  576. modemClose()
  577. {
  578.     if (cfg.IBM_or_clone && IBMinterrupts) {
  579.     outportb(INTA01, old_inta01);        /* Kill vector */
  580.     intrrest(COMINT_VEC);
  581.     }
  582. }
  583.  
  584. /************************************************************************/
  585. /*    modemInit() is responsible for all modem-related initialization */
  586. /*    at system startup                        */
  587. /*    Globals modified:    haveCarrier    visibleMode        */
  588. /*                whichIO     modStat         */
  589. /*                ExitToMsDos    justLostCarrier     */
  590. /*    modified 82Dec10 to set FDC-1 SIO-B clock speed at        */
  591. /*    300 baud     -dvm                        */
  592. /************************************************************************/
  593. modemInit()
  594. {
  595.     static char second = FALSE;
  596.  
  597.     msgBuf.Ooops    = '\n';
  598. #ifdef NEED_VISIBLE
  599.     visibleMode     = FALSE;
  600. #endif
  601.  
  602.     interpret(cfg.pInitPort);
  603.     interpret(cfg.pHangUp);
  604.     if (!second) setInterrupts();
  605.     second = TRUE;
  606.     haveCarrier = modStat = gotCarrier();
  607. }
  608.  
  609. #ifdef MSDOS
  610. setInterrupts()     /* ASSUME COM1 for now */
  611. {
  612.     unsigned char bite, inportb();
  613.     int comint();
  614.  
  615.     if (!cfg.IBM_or_clone || !IBMinterrupts) return ;
  616.     outportb(/*MCR*/0x3fc, 0x0b);
  617.     inportb(/*MSR*/cfg.modem_status, bite);
  618.     inportb(/*LSR*/cfg.line_status, bite);
  619.     inportb(/*IOR*/cfg.modem_data, bite);
  620.     bite = inportb(/*LCR*/0x3fb);
  621.     bite &= 0x7f;
  622.     outportb(/*LCR*/0x3fb, bite);
  623.     outportb(/*IER*/0x3f9, 0x01);
  624.     old_inta01 = bite = inportb(INTA01);
  625.     bite &= 0xef;
  626.     outportb(INTA01, bite);
  627.     cptr = head = 0;
  628.     intrinit(comint, 5000, COMINT_VEC);
  629. }
  630. #endif
  631.  
  632. comint()
  633. {
  634.     unsigned char inportb(), cbuf;
  635.     unsigned int  lsr_data;
  636.  
  637.     lsr_data = inportb(/*LSR*/cfg.line_status);
  638.     if ((lsr_data & 0x01) == 0) {
  639.         outportb(/*MCR*/0x3fc, 0x0b);
  640.         outportb(/*INTA00*/0x20, EOI);
  641.         return;
  642.     }
  643.     cbuf = inportb(/*RX_BUFFER*/cfg.modem_data);
  644.     if (cptr >= COMBUF_SIZE)
  645.         cptr = 0;
  646.     combuf[cptr++] = cbuf;
  647.     outportb(/*MCR*/0x3fc, 0x0b);
  648.     outportb(/*INTA00*/0x20, EOI);
  649.     return ;
  650. }
  651.  
  652.  
  653. /************************************************************************/
  654. /* modIn() toplevel modem-input function                */
  655. /*   If DCD status has changed since the last access, reports        */
  656. /*   carrier present or absent and sets flags as appropriate.        */
  657. /*   In case of a carrier loss, waits 20 ticks and rechecks        */
  658. /*   carrier to make sure it was not a temporary glitch.        */
  659. /*   If carrier is newly received, returns newCarrier = TRUE;  if    */
  660. /*   carrier lost returns 0.  If carrier is present and state        */
  661. /*   has not changed, gets a character if present and            */
  662. /*   returns it.  If a character is typed at the console,        */
  663. /*   checks to see if it is keyboard interrupt character.  If        */
  664. /*   so, prints short-form console menu and awaits next         */
  665. /*   keyboard character.                        */
  666. /* Globals modified:    carrierDetect    modStat     haveCarrier    */
  667. /*            justLostCarrier whichIO     ExitToMsDos    */
  668. /*            visibleMode                    */
  669. /* Returns:    modem or console input character,            */
  670. /*        or above special values                 */
  671. /************************************************************************/
  672. unsigned char modIn()
  673. {
  674.     unsigned char c, logVal;
  675.     unsigned long timer, temp, signal;
  676.     int  yr, dy, hr, mn;
  677.     char *mon;
  678.  
  679.     temp   = cfg.megaHz;
  680.     timer  = (HITIMEOUT * temp);
  681.     logVal = 0;
  682.     signal = (unsigned long) (HITIMEOUT * temp) / 21;
  683.     while (TRUE) {
  684.     if ((whichIO==MODEM) && (c=gotCarrier()) != modStat) {
  685.         /* carrier changed     */
  686.         if (c)  {       /* carrier present    */
  687.         if (cfg.search_baud == 1) {
  688.             if (Find_baud()) {
  689.             printf("Carr-detect\n");
  690.             warned        = FALSE;
  691.             haveCarrier    = TRUE;
  692.             modStat     = c;
  693.             newCarrier    = TRUE;
  694.             justLostCarrier = FALSE;
  695.             }
  696.             else
  697.             interpret(cfg.pHangUp);
  698.         }
  699.         else {
  700.             printf("Carr-detect\n");
  701.             interpret(cfg.pBauds[interpret(cfg.pCheckBaud)]);
  702.             logMessage(BAUD, rates[interpret(cfg.pCheckBaud)], FALSE);
  703.             warned        = FALSE;
  704.             haveCarrier     = TRUE;
  705.             modStat        = c;
  706.             newCarrier        = TRUE;
  707.             justLostCarrier = FALSE;
  708.             pause(200);
  709.         }
  710.         return(0);
  711.         } else {
  712.         pause(200);            /* confirm it's not a glitch */
  713.         if (!gotCarrier()) {    /* check again */
  714.             printf("Carr-loss\n");
  715.             logMessage(CARRLOSS, "", logVal);
  716.             haveCarrier     = FALSE;
  717.             modStat        = FALSE;
  718.             justLostCarrier = TRUE;
  719.             return(0);
  720.         }
  721.         }
  722.     }
  723.  
  724.     if (MIReady()) {
  725.         if (haveCarrier) {
  726.         c = getMod();
  727.         if (whichIO == MODEM)    return c;
  728.         }
  729.     }
  730.  
  731.     if (KBReady()) {
  732.         c = getCh();
  733.         if (whichIO == CONSOLE) return(c);
  734.         else {
  735.         if (c == SPECIAL) {
  736.             printf("CONSOLE mode\n ");
  737.             whichIO = CONSOLE;
  738.             setUp(FALSE);
  739.             warned        = FALSE;
  740.             return 0;
  741.         }
  742.         }
  743.     }
  744.  
  745.     if (cfg.netParticipant) {
  746.         getDate(&yr, &mon, &dy, &hr, &mn);
  747.         if (dy % cfg.dayDiv == 0)
  748.         if (hr == cfg.netHour - 1 && mn >= 55 && !warned
  749.                    && (haveCarrier || whichIO == CONSOLE)) {
  750.             warned = TRUE;
  751.             outFlag = IMPERVIOUS;
  752.             mPrintf("\n WARNING: System going down at %d AM\n ",
  753.                                 cfg.netHour);
  754.             outFlag = OUTOK;
  755.         }
  756.         else if (inNetTime(hr, mn)) {
  757.             if (haveCarrier || whichIO == CONSOLE) {
  758.             outFlag = IMPERVIOUS;
  759.             mPrintf("Going to network mode. Bye!\n ");
  760.             terminate(TRUE);
  761.             outFlag = OUTOK;
  762.             return 0;    /* This should do what we want    */
  763.             }
  764.             netController();
  765.             warned = FALSE;
  766.         }
  767.     }
  768.  
  769.         /* No need for speed here            */
  770.     if (!haveCarrier && whichIO != CONSOLE && cfg.dailyTimeout) {
  771.         getDate(&yr, &mon, &dy, &hr, &mn);
  772.         if (!nextDay) {
  773.         if (hr >= timeCrash) {
  774.             ExitToMsdos = TRUE;
  775.             exitValue    = TIME_EXIT;
  776.             return 0;
  777.         }
  778.         }
  779.         else {
  780.         if (dy != upDay && hr >= timeCrash) {
  781.             ExitToMsdos = TRUE;
  782.             exitValue    = TIME_EXIT;
  783.             return 0;
  784.         }
  785.         }
  786.     }
  787.  
  788.     /* check for no input.    (Short-circuit evaluation, remember!) */
  789.     if (whichIO==MODEM  &&    haveCarrier  &&  !--timer) {
  790.         mPrintf("Sleeping? Call again :-)");
  791.         interpret(cfg.pHangUp);
  792.         logVal = 't';
  793.     }
  794.     else if (whichIO==MODEM  &&  haveCarrier  &&  signal == timer)
  795.         oChar(BELL);
  796.     }
  797. }
  798.  
  799. /************************************************************************/
  800. /*    oChar() is the top-level user-output function            */
  801. /*      sends to modem port and console both                */
  802. /*      does conversion to upper-case etc as necessary        */
  803. /*      in "debug" mode, converts control chars to uppercase letters    */
  804. /*    Globals modified:    prevChar                */
  805. /************************************************************************/
  806. oChar(c)
  807. char c;
  808. {
  809.     prevChar = c;            /* for end-of-paragraph code    */
  810.     if (outFlag != OUTOK &&        /* s(kip) mode            */
  811.     outFlag != IMPERVIOUS)
  812.     return;
  813.  
  814.     if (termUpper)    c = toupper(c);
  815. #ifdef NEED_VISIBLE
  816.     if (debug)        c = visible(c);
  817. #endif
  818.     if (c == NEWLINE)    c = ' ';    /* doCR() handles real newlines */
  819.  
  820.     /* show on console            */
  821.     if (usingWCprotocol != WC_NONE) {
  822.     sendWCChar(c);
  823.     }
  824.     else if (outPut == DISK) {
  825.     putc(c, upfd);
  826.     }
  827.     else {
  828.     mputChar(c);
  829.     if (haveCarrier)
  830.         outMod(c);
  831.     }
  832. }
  833.  
  834. /************************************************************************/
  835. /*    outMod stuffs a char out the modem port             */
  836. /************************************************************************/
  837. outMod(c)
  838. unsigned char c;
  839. {
  840.     char notReady;
  841.     unsigned char inportb();
  842.     int  flag;
  843.     unsigned char regVal;
  844.  
  845.     if (!cfg.IBM_or_clone) {
  846.     writereg.ax = c;    /* AH is set automatically to 0         */
  847.     while (farcall(MOD_FUNC, BIOSSEG, &writereg, &inreg) & 0x01)
  848.         ;
  849.     }
  850.     else {
  851. #ifdef OLD_STYLE
  852.     notReady = TRUE;
  853.     while (notReady) {
  854.         regVal = inportb(cfg.line_status) & 32;
  855.         notReady = (regVal == 0);
  856.     }
  857.     outportb(cfg.modem_data, c);
  858. #else
  859.     outportb(/*MCR*/0x3fc, 0x0b);
  860. wait1:
  861.     flag = inportb(/*LSR*/ cfg.line_status);
  862.     if ((flag & 0x20) == 0) goto wait1;
  863.     outportb(/*IOR*/cfg.modem_data, c);
  864.     return(0);
  865. #endif
  866.  
  867.     }
  868. }
  869.  
  870. /************************************************************************/
  871. /*    pause() busy-waits N/100 seconds                */
  872. /************************************************************************/
  873. pause(i)
  874. int i;
  875. {
  876.     int j;
  877.  
  878. #define SECONDSFACTOR 102    /* Seems to be right for 8088 */
  879.     for (;  i;    i--) {
  880.     for (j=(SECONDSFACTOR*cfg.megaHz);  j;    j--);
  881.     }
  882. }
  883.  
  884. /************************************************************************/
  885. /*    mputChar()                            */
  886. /************************************************************************/
  887. mputChar(c)
  888. char c;
  889. {
  890.     if (c == BELL && cfg.noChat && !onConsole)
  891.     return;
  892.     if (!(whichIO == CONSOLE || onConsole) && !anyEcho)
  893.     return;
  894.     if (c != ESC &&
  895.       (echo == BOTH || (whichIO == CONSOLE && (echo != NEITHER || echoChar)))) {
  896.     bdos(6, c);
  897.     if (c == '\n')
  898.         mputChar('\r');
  899.     }
  900. }
  901.  
  902. /************************************************************************/
  903. /*    receive() gets a modem character, or times out ...        */
  904. /*    Returns:    char on success else ERROR            */
  905. /************************************************************************/
  906. int receive(seconds)
  907. int  seconds;
  908. {
  909.     unsigned  count;
  910.  
  911.     count = seconds * 500;
  912.     while (!MIReady()  &&  --count)
  913.     shortPause();
  914.     if (count && gotCarrier())    return inp();
  915.  
  916.     return(ERROR);
  917. }
  918.  
  919. /************************************************************************/
  920. /*    readFile() accepts a file from modem using Ward Christensen's    */
  921. /*    protocol.  (ie, compatable with xModem, modem7, yam, modem2...) */
  922. /*    Returns:    TRUE on successful transfer, else FALSE     */
  923. /************************************************************************/
  924. char readFile(pc)
  925. int  (*pc)();    /* pc will accept the file one character at a time.    */
  926.         /* returns ERROR on any problem, and closes the file    */
  927.         /* when handed ERROR as an argument.            */
  928. {
  929.     char      errMess[100];
  930.     int       i, firstchar, lastSector, thisSector, thisComplement, tries;
  931.     int       toterr, checksum, recVal;
  932.     unsigned char badSector, writeError;
  933.     unsigned char sectBuf[SECTSIZE];
  934.     unsigned char *nextChar;
  935. #ifndef NO_CRC
  936.     char goodCRC, errorDet;
  937.     unsigned totalCRC, temp;
  938.     char charIn;
  939.     int loCRC;
  940.     char acceptable[7];
  941.  
  942.     acceptable[0] = EOT;
  943.     acceptable[1] = SOH;
  944.     acceptable[2] = 0;
  945. #endif
  946.  
  947.     lastSector    = 0;
  948.     tries    = 0;
  949.     toterr    = 0;
  950.     writeError    = FALSE;
  951.  
  952.     while (MIReady())    inp();     /* clear garbage*/
  953. if (cfg.debug) printf("In readFile\n");     /*****/
  954.  
  955.     if (!inNet)
  956.     printf((cfg.debug) ? "Awaiting #0 (Try=0, Errs=0)  \r" :
  957.              "A WC upload in progress\n");
  958.  
  959. #ifdef NO_CRC
  960.     outMod(NAK);            /* Let's get it rolling     */
  961. #else
  962.     if (!(goodCRC = WCstartUp(CRC_START, 3, 4, &charIn, acceptable))) {
  963.     if (!WCstartUp(NAK, 7, 10, &charIn, acceptable)) {
  964.         if (cfg.debug) printf("Startup failed\n");        /*****/
  965.         return FALSE;
  966.     }
  967.     }
  968.     else printf("CRC mode\n");
  969. #endif
  970.     do {
  971.     strCpy(errMess, "Unknown");
  972.     badSector = FALSE;
  973.     /* get synchronized: */
  974. #ifdef NO_CRC
  975.     do {
  976.         firstchar = receive(10);
  977.     } while (
  978.         firstchar != SOH &&
  979.         firstchar != EOT &&
  980.         firstchar != ERROR
  981.     );
  982. #else
  983.     if (lastSector) {
  984.         do {
  985.         firstchar = receive(10);
  986.         } while (
  987.         firstchar != SOH &&
  988.         firstchar != EOT &&
  989.         firstchar != ERROR
  990.         );
  991.     }
  992.     else
  993.         firstchar = charIn;
  994. #endif
  995.     if (firstchar == ERROR) {
  996.         badSector = TRUE;
  997.         strCpy(errMess, "No data received");
  998.     }
  999.  
  1000.     if (firstchar == SOH)  {
  1001.         /* found StartOfHeader -- read sector# in: */
  1002.         thisSector        = receive (1);
  1003.         thisComplement    = receive (1);    /* 1's comp of thisSector */
  1004.  
  1005.         if ((thisSector + thisComplement) != 0xFF) {
  1006.         badSector = TRUE;
  1007.         sPrintf(errMess, "parity error on sector (%d %d)", thisSector,
  1008.                                   thisComplement);
  1009.         }
  1010.         else {
  1011.         if (thisSector == (lastSector + 1) % 256) {
  1012.             /* right sector... let's read it in */
  1013.             checksum    = 0;
  1014.             nextChar    = sectBuf;
  1015.             for (i = SECTSIZE;    i;  i--) {
  1016.             *nextChar    = receive (1);
  1017.             checksum    = (checksum + *nextChar++) & 0xFF;
  1018.             }
  1019.  
  1020.             recVal = receive (1);
  1021. #ifdef NO_CRC
  1022.             if (recVal != checksum) {
  1023.             badSector = TRUE;
  1024.   sPrintf(errMess, "checksum error on data: expected %d, received %d",
  1025.                 checksum, recVal);
  1026.             }
  1027. #else
  1028.             if (goodCRC) {
  1029.             if (recVal == ERROR) {
  1030.                 errorDet = FALSE;
  1031.                 strCpy(errMess, "recVal==ERROR on CRC");
  1032.             }
  1033.             else {
  1034.                 loCRC = receive(1);
  1035.                 if (loCRC == ERROR) {
  1036.                 errorDet = FALSE;
  1037.                 strCpy(errMess, "loCRC==ERROR on CRC");
  1038.                 }
  1039.                 else {
  1040.                 totalCRC = (recVal << 8) + loCRC;
  1041.                 temp = calcrc(sectBuf, 128);
  1042.                 errorDet = (totalCRC == temp);
  1043.     sPrintf(errMess, "totalCRC==%x, calcrc==%x, recVal==%x, loCRC==%x\n",
  1044.  totalCRC, temp, recVal, loCRC);
  1045.                 }
  1046.             }
  1047.             }
  1048.             else {
  1049.             errorDet = (recVal == checksum);
  1050.     sPrintf(errMess, "recVal==%x, checksum==%x\n", recVal, checksum);
  1051.  
  1052.             }
  1053.  
  1054.             if (!errorDet) {
  1055.             badSector = TRUE;
  1056.             }
  1057. #endif
  1058.             else {
  1059.             tries        = 0;
  1060.             lastSector    = thisSector % 256;
  1061. if (cfg.debug) printf("lastSector=%d\n", lastSector);    /****/
  1062.  
  1063.               if (cfg.debug) printf("Awaiting #%d (Try=0, Errs=%d)  \r",
  1064.                 thisSector, toterr
  1065.             );
  1066.  
  1067.             if (tries && toterr) mputChar('\n');
  1068.  
  1069.             /* write sector to where-ever: */
  1070.             nextChar = sectBuf;
  1071.             for (i=SECTSIZE;  i;  i--) {
  1072.                 if ((*pc)(*nextChar++) == ERROR) {
  1073.                 writeError = TRUE;
  1074.                 }
  1075.             }
  1076.             if (!writeError)  outMod(ACK);
  1077.             }
  1078.         } else    {
  1079.             /* not expected sector... */
  1080.             if (thisSector != lastSector) {
  1081.             badSector = TRUE;
  1082.             sPrintf(errMess, "VeryBadSector# %d", thisSector);
  1083.             }
  1084.             else {
  1085.             /* aha -- sender missed an ACK and resent last: */
  1086.             do;  while (receive(1) != ERROR);   /* eat it */
  1087.             outMod(ACK);    /* back in synch! */
  1088.             }
  1089.         }
  1090.         }    /* end of "if (thisSector + thisComplement == 255"    */
  1091.     }    /* end of "if (firstChar == SOH)"            */
  1092.  
  1093.     if (badSector)    {
  1094.         if (cfg.debug) printf("\n%s\n", errMess);
  1095.         strCpy(errMess, "Unknown");
  1096.         tries++;
  1097.         if (lastSector != 0)  toterr++;
  1098.  
  1099.         while (receive (1) != ERROR);
  1100.         if (cfg.debug) printf("Awaiting #%d (Try=%d, Errs=%d)  \r",
  1101.         lastSector, tries, toterr
  1102.         );
  1103.         if (tries && toterr) mputChar('\n');
  1104.         if (gotCarrier())
  1105.         outMod(NAK);
  1106.         else
  1107.         tries = ERRORMAX;    /* No carrier, so force out.    */
  1108.     }
  1109.     } while (
  1110.     firstchar != EOT       &&
  1111.     tries      <  ERRORMAX  &&
  1112.     !writeError
  1113.     );
  1114.  
  1115.     if (writeError) {
  1116.     printf("\nwrite error\n");
  1117.     outMod(CAN);
  1118.     }
  1119.     else if (firstchar != EOT    ||   tries >= ERRORMAX) {
  1120.     printf("\naborting\n");
  1121.     return FALSE;
  1122.     } else {
  1123.     outMod(ACK);
  1124.     if (!inNet) printf("\nfile reception complete.\n");
  1125.     return TRUE;
  1126.     }
  1127. }
  1128.  
  1129. /************************************************************************/
  1130. /*    WCstartUp() Starts up WC and (sometime) YMODEM            */
  1131. /************************************************************************/
  1132. WCstartUp(startChar, tries, timeout, charIn, lookFor)
  1133. char startChar, *charIn;
  1134. int  tries, timeout;
  1135. char *lookFor;
  1136. {
  1137.     int retVal;
  1138.     char *strchr();
  1139.  
  1140. if (cfg.debug) printf("In WCstartup\n");
  1141.     for (; tries && gotCarrier(); tries--) {
  1142. if (cfg.debug) printf("%d: %d\n", tries, startChar);
  1143.     outMod(startChar);
  1144.     retVal = receive(10);
  1145. if (cfg.debug) printf("Received %d\n", retVal);
  1146.     if (retVal != ERROR) {
  1147.         *charIn = retVal;
  1148.         if (strchr(lookFor, *charIn) != NULL)
  1149.         return TRUE;
  1150.     }
  1151.     }
  1152.     return FALSE;
  1153. }
  1154.  
  1155. /************************************************************************/
  1156. /*    ringSysop() signals a chat mode request.  Exits on input from    */
  1157. /*  modem or keyboard.                            */
  1158. /************************************************************************/
  1159. #define RING_LIMIT 6
  1160. ringSysop()
  1161. {
  1162.     char BBSCharReady(), answered;
  1163.     int  i, ring;
  1164.  
  1165.     mPrintf("\n Ringing sysop.\n ");
  1166.  
  1167.     answered = FALSE;
  1168.     for (ring = 0; ring < RING_LIMIT && !answered && gotCarrier(); ring++) {
  1169.     for (i=0;
  1170.          ring < RING_LIMIT && !BBSCharReady() && !KBReady();
  1171.          i = ++i % 7)  {
  1172.         /* play shave-and-a-haircut/two bits... as best we can: */
  1173.         oChar(BELL);
  1174.         pause(cfg.shave[i]);
  1175.         if (i == 6) ring++;
  1176.     }
  1177.     if (BBSCharReady() || KBReady()) answered = TRUE;
  1178.     }
  1179.  
  1180.     if (KBReady())   {
  1181.     getCh();
  1182.     whichIO = CONSOLE;
  1183.     interact();
  1184.     whichIO = MODEM;
  1185.     }
  1186.     else if (ring >= RING_LIMIT) {
  1187.     cfg.noChat = TRUE;
  1188.     mPrintf("\n Sorry, Sysop not around...\n ");
  1189.     }
  1190.     else modIn();
  1191. }
  1192.  
  1193. /************************************************************************/
  1194. /*    sendWCChar() sends a file using Ward Christensen's protocol.    */
  1195. /*    (i.e., compatable with xModem, modem7, modem2, YAM, ... )    */
  1196. /************************************************************************/
  1197. int sendWCChar(c)
  1198. int c;           /* character to output to MODEM, or ERROR on EOF */
  1199. {
  1200.     unsigned int  CRC;
  1201.     int       i, j, m;
  1202.     unsigned char ck;
  1203.  
  1204.     if (WCError)
  1205.     return FALSE;
  1206.  
  1207.     WCBuf[WCChar++] = c;
  1208.     if (WCChar != SECTSIZE)
  1209.     return TRUE;
  1210.  
  1211.     for (i = 0; i < ERRORMAX; i++) {    /* Time to transmit        */
  1212.     if (cfg.debug) printf("Sending #%3d (errors =%3d)\r", WCSecNum, i);
  1213.     outMod(SOH);
  1214.     outMod(WCSecNum);
  1215.     outMod(~WCSecNum);
  1216.     for (j = ck = 0; j < SECTSIZE; j++) {
  1217.         outMod(WCBuf[j]);
  1218.         ck += WCBuf[j];
  1219.     }
  1220.     if (CRCmode) {
  1221.         CRC = calcrc(WCBuf, 128);
  1222.         outMod(((CRC & 0xFF00) >> 8));
  1223.         outMod(CRC & 0x00FF);
  1224.     }
  1225.     else
  1226.         outMod(ck);
  1227.     m = receive(MINUTE);
  1228.     if (m == ACK || m == CAN || !gotCarrier()) break;
  1229.     }
  1230.     WCChar = 0;
  1231.     WCSecNum++;
  1232.     if (m == ACK)
  1233.     return TRUE;
  1234.     WCError = TRUE;
  1235.     mPrintf("Aborting\n ");
  1236.     return FALSE;
  1237. }
  1238.  
  1239. /************************************************************************/
  1240. /*    shortPause() busy-waits 2/1000 seconds    |br|            */
  1241. /*    This should fix the 1200 network problem <HAW>            */
  1242. /************************************************************************/
  1243. shortPause()
  1244. {
  1245. #define MSECFACTOR ((SECONDSFACTOR*2)/10)
  1246.     int j;
  1247.     for (j=(MSECFACTOR*cfg.megaHz); j;    j--);
  1248. }
  1249.